package IC.SymbolTables;

import java.util.HashMap;

import IC.SymbolTables.Types.*;


public class SymbolTable 
{
	protected SymbolTable _father = null;
	protected HashMap<String, Symbol> _symbols = new HashMap<String, Symbol>();
	protected String _uniqueName, _shortName;
	protected java.util.LinkedList<SymbolTable> _sons = new java.util.LinkedList<SymbolTable>();
	
	public void AddSon(SymbolTable son) { _sons.add(son); }
	
	public SymbolTable (String name, String representName, SymbolTable father) 
	{ 
		_uniqueName = name;
		_shortName = representName;
		SetFather(father);
	}
	public SymbolTable (String name, String representName) { this(name, representName, null); }
	
	private void SetFather(SymbolTable father)
	{
		_father = father;
		if(_father != null)
			_father._sons.add(this);
	}
	
	public SymbolTable GetFather() { return _father; }
	
	/**
	 * 
	 * @param name - id's name
	 * @param isStatic
	 * @return Symbol if such id exist and null otherwise
	 */
	public Symbol GetSymbol(String name, boolean isStatic)
	{
		Symbol symbol = SearchSym(name, isStatic);
		if (symbol != null)
			return symbol;
		else if (_father == null)
			return null;
		else
			return _father.GetSymbol(name, isStatic);
	}	
	
	public SymbolTable GetAppierenceTable(String name, boolean isStatic)
	{
		if (SearchSym(name, isStatic) != null)
			return this;
		else if (_father == null)
			return null;
		else 
			return _father.GetAppierenceTable(name, isStatic);
	}
	
	private Symbol SearchSym(String name, boolean isStatic)
	{
		Symbol symbol = _symbols.get(name);
		if ((symbol != null) && (isStatic == symbol.IsStatic()))
			return symbol;
		return null;
	}
	
	public String GetUniqueName() { return _uniqueName; }
	public String GetShortName() { return _shortName; }

	public Symbol SearchCurrentTable(String name) { return _symbols.get(name); } 
	
	/**
	 * 
	 * @param name - the ID name
	 * @param symbol - the Symbol representing it and keeps the isStatic value
	 * @return false if this ID (name) was found in the table
	 */
	public boolean SetSymbol(String name, Symbol symbol)
	{
			if(_symbols.containsKey(name))
				return false;
			
			_symbols.put(name, symbol);
			return true;
	}
	
	public String toString()
	{
		return String.format("%s\n%s%s%s%s%s%s%s\n%s", _uniqueName, 
				ClassesToString(), 
				FieldsToString(), 
				StaticMethodsToString(), 
				VirtualMethodsToString(), 
				ParametersToString(),
				VariblesToString(),
				ListOfSonsToString(),
				SonsToString());
	}
	
	private String SonsToString()
	{
		String ret = "";
		for (SymbolTable son : _sons)
			ret += son.toString();
		
		return ret;
	}
	
	private String ListOfSonsToString()
	{
		if (_sons.isEmpty())
			return "";
		
		
		String ret = "Children tables:";
		for (SymbolTable son : _sons)
			ret = String.format("%s %s,", ret, son._shortName);
	
		return IC.Utils.TrimLast(ret, 1) + "\n";
	}
	
	private String ClassesToString()
	{
		String ret = "";
		for (java.util.Map.Entry<String, Symbol> entry : _symbols.entrySet())
			if (entry.getValue().GetKind() == Symbol.Kind.CLASS)
				ret = String.format("%s    Class: %s\n", ret, entry.getKey());
		
		return ret;
	}
	
	private String FieldsToString()
	{
		String ret = "";
		for (java.util.Map.Entry<String, Symbol> entry : _symbols.entrySet())
			if (entry.getValue().GetKind() == Symbol.Kind.FIELD)
				ret = String.format("%s    Field: %s %s\n", ret, entry.getValue().GetType().toString(), entry.getKey());
		
		return ret;
	}
	
	private String StaticMethodsToString()
	{
		String ret = "";
		for (java.util.Map.Entry<String, Symbol> entry : _symbols.entrySet())
			if ((entry.getValue().GetKind() == Symbol.Kind.METHOD) && (entry.getValue().IsStatic()))
				ret = String.format("%s    Static method: %s %s\n", ret, entry.getKey(), entry.getValue().GetType().toString());
		
		return ret;
	}
	
	private String VirtualMethodsToString()
	{
		String ret = "";
		for (java.util.Map.Entry<String, Symbol> entry : _symbols.entrySet())
			if ((entry.getValue().GetKind() == Symbol.Kind.METHOD) && (!entry.getValue().IsStatic()))
				ret = String.format("%s    Virtual method: %s %s\n", ret, entry.getKey(), entry.getValue().GetType().toString());
	
		return ret;
	}
	
	private String ParametersToString()
	{
		String ret = "";
		for (java.util.Map.Entry<String, Symbol> entry : _symbols.entrySet())
			if (entry.getValue().GetKind() == Symbol.Kind.FORMAL)
				ret = String.format("%s    Parameter: %s %s\n", ret, entry.getValue().GetType().toString(), entry.getKey());
		
		return ret;
	}
	
	private String VariblesToString()
	{
		String ret = "";
		for (java.util.Map.Entry<String, Symbol> entry : _symbols.entrySet())
			if (entry.getValue().GetKind() == Symbol.Kind.VARIABLE)
				ret = String.format("%s    Local variable: %s %s\n", ret, entry.getValue().GetType().toString(), entry.getKey());
	
		return ret;
	}
}
